/*
 * Project: Daycare Web Application
 * Package: com.madetoordersoftware.daycare.control
 * File:    AuthenticationCtrl
 * Created: Jun 3, 2010
 */

/*
 *  Copyright (c) 2010 Made 2 Order Software, LLC
 *
 *  All rights reserved. No part of this software may be reproduced, stored in a retrieval system, or
 *  transmitted in any form or by any means, without the prior written permission of Made 2 Order Software.
 *
 *  You may not distribute the URL or redistribute the files to third parties. You may transfer the files
 *  onto a single storage device so long as you can prove ownership of the software. You may not reverse
 *  engineer, decompile, or disassemble the software or media files. You may not modify or translate the
 *  software or media, or distribute copies of the software or media without the written consent of Made
 *  2 Order Software
 */
package com.made2ordersoftware.daycare.control;

import com.made2ordersoftware.control.AbstractCtrl;
import com.made2ordersoftware.daycare.business.AuthenticationMgr;
import com.made2ordersoftware.daycare.control.util.JsfUtil;
import com.made2ordersoftware.daycare.domain.PersonEntity;
import com.made2ordersoftware.daycare.exception.UnauthorizedUserException;
import com.made2ordersoftware.exceptions.DAOException;
import com.made2ordersoftware.util.ExceptionHandler;
import java.util.ResourceBundle;
import javax.enterprise.context.RequestScoped;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.validator.ValidatorException;
import javax.inject.Inject;
import javax.inject.Named;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author David Canning <dcannin@gmail.com>
 * @version 1.0.0
 */
@Named("authentication")
@RequestScoped
public class AuthenticationCtrl extends AbstractCtrl {

    private static final Logger LOG = LoggerFactory.getLogger(AuthenticationCtrl.class);
    private Object password;
    private String password2;
    private String currentPassword;  //Used to verify when a customer is changing their password.
    private String userName;
    private PersonEntity user;
    @Inject
    private AuthenticationMgr manager;

    /**
     * Creates a new instance of <code>AuthenticationCtrl</code>
     */
    public AuthenticationCtrl() {
    }

    /**
     * Authenticates user and sets user rule.
     *
     * @return jsf page to navigate to when complete.
     */
    public String authenticateUser() {
        String nextPage = null;
        Exception exp = null;
        String message = "";

        try {
            setCurrentUser(manager.authenticateUser(getUserName(), getPassword()));
            nextPage = "/home";
        } catch (UnauthorizedUserException e) {
            exp = e;
            message = ResourceBundle.getBundle("/Bundle").getString("invalid_authentication");
            return nextPage;
        } catch (DAOException e) {
            exp = e;
            message = ResourceBundle.getBundle("/Bundle").getString("server_error_occurred");
            nextPage = "serverError";
            return nextPage;
        } finally {
            if (exp != null) {
                ExceptionHandler.handleException(exp);
                JsfUtil.addErrorMessage(message);
            }
        }
        return nextPage;
    }

    /**
     * Returns the user that is currently logged into the system and has a value set in the session map.
     *
     * @return The user that is currently logged in.
     */
    public PersonEntity getCurrentUser() {
        return (PersonEntity) getSessionMap().get("currentUser");
    }

    /**
     * Sets the authenticated user into session map with key currentUser.
     *
     * @param currentUser The CustomerEntity object used to set the the current user.
     */
    public void setCurrentUser(PersonEntity currentUser) {
        getSessionMap().remove("currentUser");
        if (currentUser != null) {
            getSessionMap().put("currentUser", currentUser);
        }
    }

    /**
     * Logs out customer by removing the session variable "currentUser."
     *
     * @return The home page.
     */
    public String performLogout() {
        setCurrentUser(null);
        getFacesContext().getExternalContext().invalidateSession();
        return "/home?faces-redirect=true";
    }

    /**
     * Forwards the customer to the main page if they try to go to the login page after already logging into
     * the system.
     *
     * @param cse The component system event specific to the UIComponent instance.
     */
    public void forwardToMainIfLoggedIn(ComponentSystemEvent cse) {
        if (isUserLoggedIn()) {
            LOG.info("User already logged in ... Forwarding to main page.");
            getFacesContext().getApplication().getNavigationHandler()
                    .handleNavigation(getFacesContext(), null,
                            "/home?faces-redirect=true");
        }
    }

    /**
     * Forwards the customer to the login page if they try to go to any page that requires authentication.
     *
     * @param cse The component system event specific to the UIComponent instance.
     */
    public void forwardToLoginIfNotLoggedIn(ComponentSystemEvent cse) {
        String viewId = getFacesContext().getViewRoot().getViewId();
        if (!isUserLoggedIn() && (viewId.startsWith("/user") || viewId.startsWith("/order"))) {
            LOG.info("User is not logged in ... Forwarding to login page.");
            getSessionMap().put("prevUrl", viewId);
            getFacesContext().getApplication().getNavigationHandler()
                    .handleNavigation(getFacesContext(), null,
                            "/login?faces-redirect=true");
        }
    }

    /**
     * Method used to set password so it can be used in validatePasswordEquality method.
     *
     * @param fc        The FacesContext instance for the current thread, or null if this thread no longer has
     *                  a FacesContext instance.
     * @param component The UIComponent associated with this request.
     * @param password1 A String representing the customer's password.
     *
     * @throws ValidatorException If password does not meet requirements specified by the UI.
     */
    public void validatePassword1(FacesContext fc, UIComponent component, Object password1)
            throws ValidatorException {
        this.password = password1;
    }

    /**
     * Method used to validate if the 2 passwords used on the registration and the change password form are
     * the same.
     *
     * @param fc        The FacesContext instance for the current thread, or null if this thread no longer has
     *                  a FacesContext instance.
     * @param component The UIComponent associated with this request.
     * @param password2 The String representing the customer's password. Should match the first password.
     *
     * @throws ValidatorException If the customer's 2 passwords do not match.
     */
    public void validatePasswordsEquality(FacesContext fc, UIComponent component, Object password2) throws ValidatorException {
        if (!(password.equals(password2))) {
            LOG.info("Passwords do not match");
            throw new ValidatorException(new FacesMessage("Passwords must match."));
        }
    }

    /**
     * Determines if the user is logged in. This is done by checking if there is a value for currentUser in
     * the session.
     *
     * @return True if currentUser is not null.
     */
    public boolean isUserLoggedIn() {
        return getSessionMap().containsKey("currentUser");
    }

    /**
     * Returns the password of the customer.
     *
     * @return The registered customer's password.
     */
    public String getPassword() {
        return (String) password;
    }

    /**
     * Sets the password of the customer.
     *
     * @param password The String representing customer's password.
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * Used to confirm the password of the registering customer. If password2 does not match password, an
     * exception should
     * be thrown.
     *
     * @return The confirming password which should match the variable password.
     */
    public String getPassword2() {
        return password2;
    }

    /**
     * Used to confirm the password of the registering customer. If password2 does not match password, an
     * exception should
     * be thrown.
     *
     * @param password2 The String representing confirming password which should match variable password.
     */
    public void setPassword2(String password2) {
        this.password2 = password2;
    }

    /**
     * Returns the customer's current password.
     *
     * @return the currentPassword
     */
    public String getCurrentPassword() {
        return currentPassword;
    }

    /**
     * Sets the customer's current password.
     *
     * @param currentPassword String representing the currentPassword to set
     */
    public void setCurrentPassword(String currentPassword) {
        this.currentPassword = currentPassword;
    }

    /**
     * @return the userName
     */
    public String getUserName() {
        return userName;
    }

    /**
     * @param userName the userName to set
     */
    public void setUserName(String userName) {
        this.userName = userName;
    }

}
